package org.teamh.gui.ingame.chat;

import java.util.List;
import java.util.ArrayList;

import org.lwjgl.input.Keyboard;
import org.teamh.gui.GUI;
import org.teamh.logic.datamodel.User;

import de.lessvoid.nifty.builder.HoverEffectBuilder;
import de.lessvoid.nifty.builder.PanelBuilder;
import de.lessvoid.nifty.controls.Label;
import de.lessvoid.nifty.controls.label.builder.LabelBuilder;
import de.lessvoid.nifty.controls.listbox.builder.ListBoxBuilder;
import de.lessvoid.nifty.elements.Element;
import de.lessvoid.nifty.elements.render.TextRenderer;
import de.lessvoid.nifty.elements.tools.TextBreak;
import de.lessvoid.nifty.screen.Screen;
import de.lessvoid.nifty.spi.render.RenderFont;
import de.lessvoid.nifty.tools.Color;
import de.lessvoid.nifty.tools.SizeValue;

/**
 * This class is responsible for the ingame chat. It supports the general
 * message handling and the displaying of the names, the team and the lives of
 * the players.
 * 
 * @author richard
 * 
 */
public class ChatManager {
	public static final int PRIVATE = 0;
	public static final int TEAM = 1;
	public static final int PUBLIC = 2;
	
	private static final String BLUE = "#0000ff";
	private static final String RED = "#ff0000";
	private static final long MESSAGE_DURATION = 5000;
	private static final int MESSAGE_WIDTH = 150;
	private static final String FONT = "verdana-48-regular.fnt";

	private static final String BLEND_FROM = "#ff";
	private static final String BLEND_TO = "#22";

	private GUI gui;
	
	private Screen ingameScreen;
	
	private int textWidth;
	private int textHeight;
	private int textLines;
	
	private ArrayList<ChatMessage> removal;
	private ArrayList<ChatMessage> upperLeftMessages;
	private ArrayList<ChatMessage> lowerLeftMessages;
	private ArrayList<ChatMessage> upperRightMessages;
	private ArrayList<ChatMessage> lowerRightMessages;
	
	private boolean show = true;

	public ChatManager(GUI gui) {
		this.gui = gui;
		
		removal = new ArrayList<ChatMessage>();
		upperLeftMessages = new ArrayList<ChatMessage>();
		lowerLeftMessages = new ArrayList<ChatMessage>();
		upperRightMessages = new ArrayList<ChatMessage>();
		lowerRightMessages = new ArrayList<ChatMessage>();
	}

	/**
	 * Creates a nifty panel, which contains all the elements for rendering the hud chat. 
	 * @return
	 */
	public PanelBuilder getGUI() {
		PanelBuilder builder = new PanelBuilder() {
			{
				// backgroundColor("#00ff00");
				height("768px");
				width(((int) (gui.getWidth() * 768f / gui.getHeight()) - 260)
						+ "px");
				
				childLayoutCenter();
				
				//total panel for displaying chat messages in speech bubbles and player names
				panel(new PanelBuilder() {{
					childLayoutHorizontal();
					
					width(((int) (gui.getWidth() * 768f / gui.getHeight()) - 260)
							+ "px");
					height("768");
					
					// left total panel
					panel(new PanelBuilder() {
						{
							// backgroundColor("#0000ff88");
							width("50%");
							height("*");

							childLayoutVertical();

							// upper left name panel
							panel(new PanelBuilder() {
								{
									style("nifty-panel-simple");
									width("*");
									height("25px");
									alignLeft();
									childLayoutHorizontal();
									control(new LabelBuilder("name_upper_left_player") {
										{
											visibleToMouse();
											font("verdana-small-regular.fnt");
											color("#000000");
											width("120px");
										}
									});
									
									control(new LabelBuilder("team_upper_left_player") {
										{
											visibleToMouse();
											font("verdana-small-regular.fnt");
											color("#000000");
											width("120px");
										}
									});
									
									control(new LabelBuilder("upper_left_player_lives") {
										{
											visible(false);
											text("lives: ");
											font("verdana-small-regular.fnt");
											color("#000000");
											width("50px");
										}
									});
									
									control(new LabelBuilder("upper_left_player_live") {
										{
											font("verdana-small-regular.fnt");
											color("#000000");
											width("50px");
										}
									});
								}
							});

							panel(new PanelBuilder("upper_left_chat") {
								{
									// backgroundColor("#ff00ff88");
									width("*");
									height("*");
									childLayoutAbsolute();
								}
							});

							panel(new PanelBuilder("lower_left_chat") {
								{
									// backgroundColor("#0000ff88");
									width("*");
									height("*");
									childLayoutAbsolute();
								}
							});

							// lower left name panel
							panel(new PanelBuilder() {
								{
									style("nifty-panel-simple");
									width("*");
									height("25px");
									alignLeft();
									childLayoutHorizontal();
									control(new LabelBuilder("name_lower_left_player") {
										{
											visibleToMouse();
											font("verdana-small-regular.fnt");
											color("#000000");
											width("120px");
										}
									});
									
									control(new LabelBuilder("team_lower_left_player") {
										{
											visibleToMouse();
											font("verdana-small-regular.fnt");
											color("#000000");
											width("120px");
										}
									});
									
									control(new LabelBuilder("lower_left_player_lives") {
										{
											visible(false);
											text("lives: ");
											font("verdana-small-regular.fnt");
											color("#000000");
											width("50px");
										}
									});
									
									control(new LabelBuilder("lower_left_player_live") {
										{
											font("verdana-small-regular.fnt");
											color("#000000");
											width("50px");
										}
									});
								}
							});
						}
					});
					//end left total panel
					
					// right total panel
					panel(new PanelBuilder() {
						{
							// backgroundColor("#ff00ff88");
							width("50%");
							height("*");

							childLayoutVertical();

							// upper right name panel
							panel(new PanelBuilder() {
								{
									style("nifty-panel-simple");
									width("*");
									height("25px");
									alignRight();
									childLayoutHorizontal();
									control(new LabelBuilder("name_upper_right_player") {
										{
											visibleToMouse();
											font("verdana-small-regular.fnt");
											color("#000000");
											width("120px");
										}
									});
									
									control(new LabelBuilder("team_upper_right_player") {
										{
											visibleToMouse();
											font("verdana-small-regular.fnt");
											color("#000000");
											width("120px");
										}
									});
									
									control(new LabelBuilder("upper_right_player_lives") {
										{
											visible(false);
											text("lives: ");
											font("verdana-small-regular.fnt");
											color("#000000");
											width("50px");
										}
									});
									
									control(new LabelBuilder("upper_right_player_live") {
										{
											font("verdana-small-regular.fnt");
											color("#000000");
											width("50px");
										}
									});
								}
							});

							panel(new PanelBuilder("upper_right_chat") {
								{
									// backgroundColor("#ff00ff88");
									width("*");
									height("*");
									childLayoutAbsolute();
								}
							});

							panel(new PanelBuilder("lower_right_chat") {
								{
									// backgroundColor("#0000ff88");
									width("*");
									height("*");
									childLayoutAbsolute();
								}
							});

							// lower right name panel
							panel(new PanelBuilder() {
								{
									style("nifty-panel-simple");
									width("*");
									height("25px");
									alignRight();
									childLayoutHorizontal();
									control(new LabelBuilder("name_lower_right_player") {
										{
											visibleToMouse();
											font("verdana-small-regular.fnt");
											color("#000000");
											width("120px");
										}
									});
									
									control(new LabelBuilder("team_lower_right_player") {
										{
											visibleToMouse();
											font("verdana-small-regular.fnt");
											color("#000000");
											width("120px");
										}
									});
									
									control(new LabelBuilder("lower_right_player_lives") {
										{
											visible(false);
											text("lives: ");
											font("verdana-small-regular.fnt");
											color("#000000");
											width("50px");
										}
									});
									
									control(new LabelBuilder("lower_right_player_live") {
										{
											font("verdana-small-regular.fnt");
											color("#000000");
											width("50px");
										}
									});
								}
							});
						}
					});
					//end right total panel
				}});
				//end total speech bubble panel
				
				//start chatbox panel
				panel(new PanelBuilder("chatbox_panel") {{
					childLayoutCenter();
					
					width(((int) (gui.getWidth() * 768f / gui.getHeight()) - 260)
							+ "px");
					height("768px");
					
					panel(new PanelBuilder() {{
						childLayoutVertical();
						alignCenter();
						style("nifty-panel");
						height("400px");
						width("550px");
						control(new ListBoxBuilder("chatBox") {{
							style("nifty-listbox");
							displayItems(15);
							selectionModeDisabled();
							hideHorizontalScrollbar();
							optionalVerticalScrollbar();
							width("520px");
						}});
						control(new LabelBuilder() {{
							text("Press F1 to close");
							color("#ffffff");
							width("400px");
						}});
					}});
				}});
				//end chatbox panel
			}
		};

		return builder;
	}

	/**
	 * Updates the lifes for any user
	 */
	public void updateLives() {
		int loserCount = 0;
		for(User u: gui.getStart().getLogic().getManager().getUsers().values()) {
			if(u.getUserAssets() != null) {
				setLife(u.getNickname(), u.getUserAssets().getLives());
			
				if(u.getUserAssets().getTile() != null && u.getUserAssets().getLives() <= 0) {
					if(!u.getUserAssets().isLoser()) {
						loserCount++;
						u.getUserAssets().setLoser(true);
						gui.getIngameScreen().getIngameScreenHUD().getController().setLoserWinnerAnimationOnTile(u.getUserAssets().getTile().getPosition(), "Loser");
					}
				}	
			}
		}
		
		if(loserCount == gui.getStart().getLogic().getGame().getNumPlayers() - 1) {
			for(User u: gui.getStart().getLogic().getManager().getUsers().values()) {
				if(u.getUserAssets() != null) {
					if(u.getUserAssets().getTile() != null && u.getUserAssets().getLives() > 0) {
						gui.getIngameScreen().getIngameScreenHUD().getController().setLoserWinnerAnimationOnTile(u.getUserAssets().getTile().getPosition(), "Winner");
					}
				}
			}
		}
	}

	/**
	 * Update the life for the specified user
	 * @param playerName
	 * @param lives
	 */
	private void setLife(String playerName, int lives) {
		if(ingameScreen == null) {
			return;
		}
		
		if(playerName == null) {
			return;
		}
		
		if(ingameScreen.findNiftyControl("name_upper_left_player", Label.class).getText().equals(playerName)) {
			ingameScreen.findNiftyControl("upper_left_player_live", Label.class).setText(lives + "");
		} else if(ingameScreen.findNiftyControl("name_lower_left_player", Label.class).getText().equals(playerName)) {
			ingameScreen.findNiftyControl("lower_left_player_live", Label.class).setText(lives + "");
		} else if(ingameScreen.findNiftyControl("name_upper_right_player", Label.class).getText().equals(playerName)) {
			ingameScreen.findNiftyControl("upper_right_player_live", Label.class).setText(lives + "");
		} else if(ingameScreen.findNiftyControl("name_lower_right_player", Label.class).getText().equals(playerName)) {
			ingameScreen.findNiftyControl("lower_right_player_live", Label.class).setText(lives + "");
		}
	}
	
	/**
	 * Set the upper left player name and team. 
	 * @param name
	 * @param team
	 */
	public void setUpperLeftPlayer(String name, String team, int lives) {
		checkIngameScreen();
		
		if(name == null || name.equals("")) {
			ingameScreen.findElementByName("upper_left_player_lives").hide();
			ingameScreen.findElementByName("upper_left_player_live").hide();
		} else {
			ingameScreen.findElementByName("upper_left_player_lives").show();
			ingameScreen.findElementByName("upper_left_player_live").show();
			ingameScreen.findNiftyControl("upper_left_player_live", Label.class).setText(lives + "");
		}
		
		ingameScreen.findNiftyControl("name_upper_left_player", Label.class).setText(name);
		ingameScreen.findNiftyControl("team_upper_left_player", Label.class).setText(team);
		ingameScreen.findElementByName("namePlayer1").getRenderer(TextRenderer.class).setText(name+" "+team);
	}

	/**
	 * Set the upper right player name and team
	 * @param name
	 * @param team
	 */
	public void setUpperRightPlayer(String name, String team, int lives) {
		checkIngameScreen();
		
		if(name == null || name.equals("")) {
			ingameScreen.findElementByName("upper_right_player_lives").hide();
			ingameScreen.findElementByName("upper_right_player_live").hide();
		} else {
			ingameScreen.findElementByName("upper_right_player_lives").show();
			ingameScreen.findElementByName("upper_right_player_live").show();
			ingameScreen.findNiftyControl("upper_right_player_live", Label.class).setText(lives + "");
		}
		
		ingameScreen.findNiftyControl("name_upper_right_player", Label.class).setText(name);
		ingameScreen.findNiftyControl("team_upper_right_player", Label.class).setText(team);
		ingameScreen.findElementByName("namePlayer2").getRenderer(TextRenderer.class).setText(name+" "+team);
	}

	/**
	 * Set the lower left player name and team. 
	 * @param name
	 * @param team
	 */
	public void setLowerLeftPlayer(String name, String team, int lives) {
		checkIngameScreen();
		
		if(name == null || name.equals("")) {
			ingameScreen.findElementByName("lower_left_player_lives").hide();
			ingameScreen.findElementByName("lower_left_player_live").hide();
		} else {
			ingameScreen.findElementByName("lower_left_player_lives").show();
			ingameScreen.findElementByName("lower_left_player_live").show();
			ingameScreen.findNiftyControl("lower_left_player_live", Label.class).setText(lives + "");
		}
		
		ingameScreen.findNiftyControl("name_lower_left_player", Label.class).setText(name);
		ingameScreen.findNiftyControl("team_lower_left_player", Label.class).setText(team);
		ingameScreen.findElementByName("namePlayer3").getRenderer(TextRenderer.class).setText(name+" "+team);
	}

	/**
	 * Set the lower right player name and team. 
	 * @param name
	 * @param team
	 */
	public void setLowerRightPlayer(String name, String team, int lives) {
		checkIngameScreen();
		
		if(name == null || name.equals("")) {
			ingameScreen.findElementByName("lower_right_player_lives").hide();
			ingameScreen.findElementByName("lower_right_player_live").hide();
		} else {
			ingameScreen.findElementByName("lower_right_player_lives").show();
			ingameScreen.findElementByName("lower_right_player_live").show();
			ingameScreen.findNiftyControl("lower_right_player_live", Label.class).setText(lives + "");
		}
		
		ingameScreen.findNiftyControl("name_lower_right_player", Label.class).setText(name);
		ingameScreen.findNiftyControl("team_lower_right_player", Label.class).setText(team);
		ingameScreen.findElementByName("namePlayer4").getRenderer(TextRenderer.class).setText(name+" "+team);
	}
	
	/**
	 * Checks, if the ingamescreen variable is set. If not, it is set. This must
	 * be done for fast access to the screen.
	 */
	private void checkIngameScreen() {
		if(ingameScreen == null) {
			ingameScreen = gui.getNifty().getScreen("ingameScreen");
		}
	}

	/**
	 * After calling this method, a chatmessage will be displayed on the screen.
	 * It will be set correctly to the player, if the player already chosen a
	 * start tile. The visibility will be underlined by the same color, which is
	 * already used by the lobby.
	 * 
	 * @param sender
	 * @param receiver
	 * @param message
	 * @param visibility
	 */
	public void displayMessage(String sender, String receiver, String message, int visibility) {
		checkIngameScreen();
		
		if(ingameScreen.findNiftyControl("name_upper_left_player", Label.class).getText().equals(sender)) {
			setUpperLeftChatMessage(message, receiver, visibility);
		} else if(ingameScreen.findNiftyControl("name_lower_left_player", Label.class).getText().equals(sender)) {
			setLowerLeftChatMessage(message, receiver, visibility);
		} else if(ingameScreen.findNiftyControl("name_upper_right_player", Label.class).getText().equals(sender)) {
			setUpperRightChatMessage(message, receiver, visibility);
		} else if(ingameScreen.findNiftyControl("name_lower_right_player", Label.class).getText().equals(sender)) {
			setLowerRightChatMessage(message, receiver, visibility);
		} else {
			//visitor sended the message
			if(!show) {
				setInfoMessage();
			}
		}
		
		addMessageToChatBox(sender, receiver, message, visibility);
	}
	
	/**
	 * This method displays the info message. That means, if a player, who does
	 * not selected a start tile writes a message, this info panel shows a
	 * little "F1" to the player. The user knows then, that a message in the
	 * message panel appeared.
	 */
	private void setInfoMessage() {
		Element panel = ingameScreen.findElementByName("lower_right_chat");
		
		String id = Math.random() + "";
		
		Element info = getInfoPanel(id, panel);
		info.setConstraintX(new SizeValue((panel.getWidth() - info.getWidth()) + "px"));
		info.setConstraintY(new SizeValue((-info.getHeight() / 2) + "px"));
		
		panel.layoutElements();
		
		lowerRightMessages.add(new ChatMessage(id, 0, 1000));
	}
	
	/**
	 * Adds a chat message to the lower right player. 
	 * @param message
	 * @param receiver
	 * @param visibility
	 */
	private void setLowerRightChatMessage(String message, String receiver, int visibility) {
		checkIngameScreen();
		
		//get the panel, where the message should be added to
		Element panel = ingameScreen.findElementByName("lower_right_chat");
		int height = panel.getHeight();
		
		//generate a random id
		String id = Math.random() + "";
		//create a new message panel
		Element messagePanel = getChatMessagePanel(false, message, receiver, panel, id, visibility);
		
		//Shift any chat message up
		for(ChatMessage m: lowerRightMessages) {
			Element el = ingameScreen.findElementByName(m.getId());
			int newValue = (int)el.getConstraintY().getValue(1) - messagePanel.getHeight();
			if(newValue < 0) {
				removal.add(m);
				el.setVisible(false);
			}
			el.setConstraintY(new SizeValue(newValue + "px"));
		}
		
		//set the position of the new chat message
		messagePanel.setConstraintX(new SizeValue((panel.getWidth() - messagePanel.getWidth() - 30) + "px"));
		messagePanel.setConstraintY(new SizeValue((height - messagePanel.getHeight()) + "px"));
		
		//this must be called due to the correct positioning
		panel.layoutElements();
		
		//message is added to both. 
		panel.add(messagePanel);
		lowerRightMessages.add(new ChatMessage(id, messagePanel.getHeight(), MESSAGE_DURATION));
	}
	
	/**
	 * Adds a chatmessage to the lower left player. 
	 * @param message
	 * @param receiver
	 * @param visibility
	 */
	private void setLowerLeftChatMessage(String message, String receiver, int visibility) {
		checkIngameScreen();
		
		//get the panel, where the message should be added to
		Element panel = ingameScreen.findElementByName("lower_left_chat");
		int height = panel.getHeight();
		
		//generate a random id
		String id = Math.random() + "";
		//create a new message panel
		Element messagePanel = getChatMessagePanel(false, message, receiver, panel, id, visibility);
		
		//Shift any chat message up
		for(ChatMessage m: lowerLeftMessages) {
			Element el = ingameScreen.findElementByName(m.getId());
			int newValue = (int)el.getConstraintY().getValue(1) - messagePanel.getHeight();
			if(newValue < 0) {
				removal.add(m);
				el.setVisible(false);
			}
			el.setConstraintY(new SizeValue(newValue + "px"));
		}
		
		//set the position of the new chat message
		messagePanel.setConstraintX(new SizeValue((30) + "px"));
		messagePanel.setConstraintY(new SizeValue((height - messagePanel.getHeight()) + "px"));
		
		//this must be called due to the correct positioning
		panel.layoutElements();
		
		//message is added to both. 
		panel.add(messagePanel);
		lowerLeftMessages.add(new ChatMessage(id, messagePanel.getHeight(), MESSAGE_DURATION));
	}
	
	/**
	 * Adds a chat message to the upper right player. 
	 * @param message
	 * @param receiver
	 * @param visibility
	 */
	private void setUpperRightChatMessage(String message, String receiver, int visibility) {
		checkIngameScreen();
		
		//get the panel, where the message should be added to
		Element panel = ingameScreen.findElementByName("upper_right_chat");
		int height = panel.getHeight();
		
		//generate a random id
		String id = Math.random() + "";
		//create a new message panel
		Element messagePanel = getChatMessagePanel(true, message, receiver, panel, id, visibility);
		
		//Shift any chat message up
		for(ChatMessage m: upperRightMessages) {
			Element el = ingameScreen.findElementByName(m.getId());
			int newValue = (int)el.getConstraintY().getValue(1) + messagePanel.getHeight();
			if(newValue + el.getHeight() > height) {
				removal.add(m);
				el.setVisible(false);
			}
			el.setConstraintY(new SizeValue(newValue + "px"));
		}
		
		//set the position of the new chat message
		messagePanel.setConstraintX(new SizeValue((panel.getWidth() - messagePanel.getWidth() - 30) + "px"));
		messagePanel.setConstraintY(new SizeValue((0) + "px"));
		
		//this must be called due to the correct positioning
		panel.layoutElements();
		
		//message is added to both. 
		panel.add(messagePanel);
		upperRightMessages.add(new ChatMessage(id, messagePanel.getHeight(), MESSAGE_DURATION));
	}
	
	/**
	 * Adds a chat message to the upper left player. 
	 * @param message
	 * @param receiver
	 * @param visibility
	 */
	private void setUpperLeftChatMessage(String message, String receiver, int visibility) {
		checkIngameScreen();
		
		//get the panel, where the message should be added to
		Element panel = ingameScreen.findElementByName("upper_left_chat");
		int height = panel.getHeight();
		
		//generate a random id
		String id = Math.random() + "";
		//create a new message panel
		Element messagePanel = getChatMessagePanel(true, message, receiver, panel, id, visibility);
		
		//Shift any chat message up
		for(ChatMessage m: upperLeftMessages) {
			Element el = ingameScreen.findElementByName(m.getId());
			int newValue = (int)el.getConstraintY().getValue(1) + messagePanel.getHeight();
			if(newValue + el.getHeight() > height) {
				removal.add(m);
				el.setVisible(false);
			}
			el.setConstraintY(new SizeValue(newValue + "px"));
		}
		
		//set the position of the new chat message
		messagePanel.setConstraintX(new SizeValue((30) + "px"));
		messagePanel.setConstraintY(new SizeValue((0) + "px"));
		
		//this must be called due to the correct positioning
		panel.layoutElements();
		
		//message is added to both. 
		panel.add(messagePanel);
		upperLeftMessages.add(new ChatMessage(id, messagePanel.getHeight(), MESSAGE_DURATION));
	}

	/**
	 * This method creates a nifty speech bubble, which shows a message of a player with a starttile. 
	 * @param top
	 * @param message
	 * @param receiver
	 * @param parent
	 * @param id
	 * @param visibility
	 * @return
	 */
	private Element getChatMessagePanel(final boolean top, String message, String receiver, Element parent, String id, int visibility) {
		RenderFont font = gui.getNifty().getRenderEngine().createFont(FONT);
		textWidth = MESSAGE_WIDTH;
		textHeight = font.getHeight();
		textLines = 1;
		final StringBuffer buffer = new StringBuffer();
		
		TextBreak b = new TextBreak(getMessage(message, receiver, visibility), MESSAGE_WIDTH, font);
		List<String> list = b.split();
		
		textLines = list.size();
		
		final String color = getColor(visibility);
		
		int count = 0;
		for(String s: list) {
			buffer.append(s);
			if(count < list.size() - 1) {
				buffer.append("\n");
			}
			count++;
		}
		
		PanelBuilder builder = new PanelBuilder(id) {
			{
				visibleToMouse();
				onStartHoverEffect(new HoverEffectBuilder("fade") {
					{
						effectParameter("start", BLEND_FROM);
						effectParameter("end", BLEND_TO);
						effectParameter("length", "300");
					}
				});
				onHoverEffect(new HoverEffectBuilder("fade") {
					{
						effectParameter("start", BLEND_FROM);
						effectParameter("end", BLEND_TO);
					}
				});
				onEndHoverEffect(new HoverEffectBuilder("fade") {
					{
						effectParameter("start", BLEND_TO);
						effectParameter("end", BLEND_FROM);
						effectParameter("length", "300");
					}
				});
				if(top) {
					style("nifty-chatbox-top");
				} else {
					style("nifty-chatbox-bottom");
				}
				
				childLayoutCenter();
				alignCenter();
				width((textWidth + 40) + "px");
				height(((textHeight * textLines) + 50) + "px");
				control(new LabelBuilder() {{
					text(buffer.toString());
					font(FONT);
					color(color);
					width((textWidth + 1) + "px");
					height((textHeight * textLines + 1) + "px");
				}});
			}
		};
		
		Element widget = builder.build(gui.getNifty(), ingameScreen, parent);
		
		return widget;
	}
	
	/**
	 * This method creates the infopanel, which displays "F1" to the screen. 
	 * @param id
	 * @param parent
	 * @return
	 */
	private Element getInfoPanel(String id, Element parent) {
		PanelBuilder panel = new PanelBuilder(id) {{
			visibleToMouse();
			onStartHoverEffect(new HoverEffectBuilder("fade") {
				{
					effectParameter("start", BLEND_FROM);
					effectParameter("end", BLEND_TO);
					effectParameter("length", "300");
				}
			});
			onHoverEffect(new HoverEffectBuilder("fade") {
				{
					effectParameter("start", BLEND_FROM);
					effectParameter("end", BLEND_TO);
				}
			});
			onEndHoverEffect(new HoverEffectBuilder("fade") {
				{
					effectParameter("start", BLEND_TO);
					effectParameter("end", BLEND_FROM);
					effectParameter("length", "300");
				}
			});
			
			style("nifty-panel-simple");
			childLayoutCenter();
			width("22px");
			height("22px");
			
			control(new LabelBuilder() {{
				text("F1");
				color("#000000");
				width("400px");
			}});
		}};
		
		Element widget = panel.build(gui.getNifty(), ingameScreen, parent);
		
		return widget;
	}
	
	/**
	 * This method transforms a message to a String, which includes the
	 * information of visibility and who received the message.
	 * 
	 * @param message
	 * @param receiver
	 * @param visibility
	 * @return
	 */
	private String getMessage(String message, String receiver, int visibility) {
		switch(visibility) {
		case PRIVATE: return "<" + receiver + "> " + message;
		case TEAM: return "<" + receiver + "> " + message;
		default: return message;
		}
		
	}
	
	/**
	 * Returns the color in dependence of the visibility. 
	 * @param visibility
	 * @return
	 */
	private String getColor(int visibility) {
		switch(visibility) {
		case PRIVATE: return BLUE;
		case TEAM: return RED;
		default: return "#000000";
		}
	}
	
	/**
	 * This update method must be called within the render loop of the program
	 * to update the current chat messages. This method is mainly responsible
	 * for the correct removement of the messages.
	 */
	public void update() {
		for(ChatMessage m: upperLeftMessages) {
			if(m.delete()) {
				removal.add(m);
			}
		}
		
		for(ChatMessage m: lowerLeftMessages) {
			if(m.delete()) {
				removal.add(m);
			}
		}
		
		for(ChatMessage m: upperRightMessages) {
			if(m.delete()) {
				removal.add(m);
			}
		}
		
		for(ChatMessage m: lowerRightMessages) {
			if(m.delete()) {
				removal.add(m);
			}
		}
		
		for(ChatMessage m: removal) {
			Element e = ingameScreen.findElementByName(m.getId());
			remove(e);
			
			upperLeftMessages.remove(m);
			lowerLeftMessages.remove(m);
			upperRightMessages.remove(m);
			lowerRightMessages.remove(m);
		}
		
		removal.clear();
	}
	
	/**
	 * This method removes a nifty element. This cannot only be done 
	 * by mark for removal, due to the unknown error of multiple 
	 * ids within the elements. 
	 * @param element
	 */
	private void remove(Element element) {
		Element parent = element.getParent();
		String id = element.getId();
		
		for(Element e: parent.getElements()) {
			if(e.getId().equals(id)) {
				e.markForRemoval();
			}
		}
	}
	
	/**
	 * This method is called, if a key is pressed. 
	 * @param key
	 */
	public void keyPressed(int key) {
		switch(key) {
		case Keyboard.KEY_F1: toggleShow(); break;
		}
	}
	
	/**
	 * This method must be called, if the visibility of the message panel is
	 * toggled. This is necessary to ensure, that there is only one instance of
	 * the popup visible.
	 */
	public void toggleShow() {
		checkIngameScreen();
		
		show = !show;
		Element e = ingameScreen.findElementByName("chatbox_panel");
		
		if(show) {
			e.show();
		} else {
			e.hide();
		}
	}
	
	/**
	 * This method adds a chat message to the chatbox panel, which shows all the
	 * messages in the order of income.
	 * 
	 * @param sender
	 * @param receiver
	 * @param message
	 * @param visibility
	 */
	private void addMessageToChatBox(String sender, String receiver, String message, int visibility) {
		Color color = new Color(getColor(visibility));
		
		if(visibility != PUBLIC) {
			message = sender + " -> " + receiver + ": " + message;
		} else {
			message = sender + ": " + message;
		}
		
		gui.getIngameScreen().getIngameScreenHUD().getController().addMessageToChatBox(message, color);
	}
	
	/**
	 * Returns, if the chatbox panel is currently visible. 
	 * @return
	 */
	public boolean isShowingChatbox() {
		return show;
	}
}